package com.ssd.handler.wechat;

import cn.hutool.core.convert.Convert;
import cn.hutool.core.map.MapUtil;
import cn.hutool.core.util.NumberUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;


import com.ssd.api.payment.dto.enums.PayChannelEnum;
import com.ssd.api.payment.dto.enums.RefundStatusEnum;
import com.ssd.api.payment.dto.enums.TradingStateEnum;
import com.ssd.exception.LogicException;
import com.ssd.annotation.PayChannel;
import com.ssd.constant.TradingConstant;
import com.ssd.enums.TradingEnum;
import com.ssd.handler.BasicPayHandler;
import com.ssd.handler.wechat.response.WeChatResponse;
import com.ssd.model.domain.RefundRecord;
import com.ssd.model.domain.Trading;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;

import java.util.Map;

import static com.ssd.enums.TradingEnum.NATIVE_REFUND_FAIL;

/**
 * 微信基础支付功能的实现
 * @author cy
 * @version 1.0
 */
@Slf4j
@Component("weChatBasicPayHandler")
@PayChannel(type = PayChannelEnum.WECHAT_PAY)
public class WeChatBasicPayHandler implements BasicPayHandler {

    /**
     * 查询交易状态
     * 通过调用微信支付接口查询交易的当前状态，并根据查询结果更新交易对象的状态
     * @param trading 交易对象，包含查询所需的信息如商户订单号等
     * @return 返回一个布尔值，表示交易状态是否已更新
     * @throws LogicException 当调用微信支付接口失败或解析响应出错时抛出
     */
    @Override
    public Boolean queryTrading(Trading trading) throws LogicException {
        // 获取微信支付的client对象
        WechatPayHttpClient client = WechatPayHttpClient.get(trading.getEnterpriseId());

        // 请求地址：交易号
        String apiPath = StrUtil.format("/v3/pay/transactions/out-trade-no/{}", trading.getTradingOrderNo());

        // 请求参数
        Map<String, Object> params = MapUtil.<String, Object>builder()
                .put("mchid", client.getMchId())//商户号
                .build();

        WeChatResponse response;
        try {
            response = client.doGet(apiPath, params);
        } catch (Exception e) {
            // 当调用微信接口出错时，记录日志并抛出自定义异常
            log.error("调用微信接口出错！apiPath = {}, params = {}", apiPath, JSONUtil.toJsonStr(params), e);
            throw new LogicException( NATIVE_REFUND_FAIL.getValue());
        }
        if (response.isOk()) {
            JSONObject jsonObject = JSONUtil.parseObj(response.getBody());
            // 获取交易状态
            String tradeStatus = jsonObject.getStr("trade_state");
            // 根据交易状态更新交易对象的状态
            if (StrUtil.equalsAny(tradeStatus, TradingConstant.WECHAT_TRADE_CLOSED, TradingConstant.WECHAT_TRADE_REVOKED)) {
                //如果状态是已关闭或已撤销，就设置状态为取消订单
                trading.setTradingState(TradingStateEnum.QXDD);
            } else if (StrUtil.equalsAny(tradeStatus, TradingConstant.WECHAT_TRADE_SUCCESS, TradingConstant.WECHAT_TRADE_REFUND)) {
                //如果状态是已支付或已退款，就设置状态为已付款
                trading.setTradingState(TradingStateEnum.YJS);
            } else if (StrUtil.equalsAny(tradeStatus, TradingConstant.WECHAT_TRADE_PAYERROR)) {
                //如果状态是支付失败，就设置状态为付款失败
                trading.setTradingState(TradingStateEnum.FKSB);
            } else {
                // 非最终状态不处理
                return false;
            }
            // 更新交易单的其他相关信息
            trading.setTransactionId(jsonObject.getStr("transaction_id"));//第三方支付的交易号
            trading.setResultCode(tradeStatus);//第三方交易返回编码【最终确认交易结果】
            trading.setResultMsg(jsonObject.getStr("trade_state_desc"));//第三方交易返回提示消息【最终确认交易信息】
            trading.setResultJson(response.getBody());//第三方交易返回信息json【分析交易最终信息】
            return true;
        }
        // 当响应不正常时，抛出自定义异常
        throw new LogicException( NATIVE_REFUND_FAIL.getValue());
    }

    /**
     * 关闭交易订单
     *
     * 本方法通过调用微信支付接口来关闭一个已经创建的交易订单
     * 它首先构建微信支付所需的请求参数，然后发送请求，最后根据响应结果更新交易状态
     *
     * @param trading 交易对象，包含交易相关信息，如交易订单号和企业ID
     * @return 返回一个布尔值，表示交易订单是否成功关闭
     * @throws LogicException 当关闭交易过程中发生错误时抛出此异常
     */
    @Override
    public Boolean closeTrading(Trading trading) throws LogicException {
        // 获取微信支付的client对象
        WechatPayHttpClient client = WechatPayHttpClient.get(trading.getEnterpriseId());
        // 请求地址，使用格式化字符串构建具体的API路径：交易号
        String apiPath = StrUtil.format("/v3/pay/transactions/out-trade-no/{}/close", trading.getTradingOrderNo());
        // 请求参数，包括商户号
        Map<String, Object> params = MapUtil.<String, Object>builder()
                .put("mchid", client.getMchId())
                .build();
        try {
            // 发起请求并获取响应
            WeChatResponse response = client.doPost(apiPath, params);
            // 根据响应状态码判断交易是否成功关闭
            if (response.getStatus() == 204) {
                // 更新交易状态为取消订单
                trading.setTradingState(TradingStateEnum.QXDD);
                return true;
            }
            return false;
        } catch (Exception e) {
            // 当发生异常时，抛出自定义异常
            throw new LogicException( TradingEnum.CLOSE_FAIL.getValue());
        }
    }

    /**
     * 重写退款交易方法
     * @param refundRecord 退款记录对象，包含退款所需的信息
     * @return 退款操作是否成功的布尔值
     * @throws LogicException 如果退款失败，则抛出此异常
     */
    @Override
    public Boolean refundTrading(RefundRecord refundRecord) throws LogicException {
        // 获取微信支付的client对象
        WechatPayHttpClient client = WechatPayHttpClient.get(refundRecord.getEnterpriseId());
        // 请求地址
        String apiPath = "/v3/refund/domestic/refunds";
        // 请求参数
        Map<String, Object> params = MapUtil.<String, Object>builder()
                .put("out_refund_no", Convert.toStr(refundRecord.getRefundNo()))//本次退款订单号
                .put("out_trade_no", Convert.toStr(refundRecord.getTradingOrderNo()))//交易单
                .put("amount", MapUtil.<String, Object>builder()
                        .put("refund", NumberUtil.mul(refundRecord.getRefundAmount(), 100)) // 本次退款金额
                        .put("total", NumberUtil.mul(refundRecord.getTotal(), 100)) // 原订单金额
                        .put("currency", "CNY") // 币种
                        .build())
                .build();
        WeChatResponse response;
        try {
            response = client.doPost(apiPath, params);
        } catch (Exception e) {
            // 记录错误日志并抛出公共异常
            log.error("调用微信接口出错！apiPath = {}, params = {}", apiPath, JSONUtil.toJsonStr(params), e);
            throw new LogicException( NATIVE_REFUND_FAIL.getValue());
        }
        // 设置退款记录的退款代码和消息
        refundRecord.setRefundCode(Convert.toStr(response.getStatus()));
        refundRecord.setRefundMsg(response.getBody());
        if (response.isOk()) {
            // 解析响应体为JSON对象
            JSONObject jsonObject = JSONUtil.parseObj(response.getBody());
            // 设置微信退款ID
            refundRecord.setRefundId(jsonObject.getStr("refund_id"));
            // 根据退款状态更新退款记录的状态
            String status = jsonObject.getStr("status");
            if (StrUtil.equals(status, TradingConstant.WECHAT_REFUND_PROCESSING)) {
                refundRecord.setRefundStatus(RefundStatusEnum.SENDING);//退款中状态
            } else if (StrUtil.equals(status, TradingConstant.WECHAT_REFUND_SUCCESS)) {
                refundRecord.setRefundStatus(RefundStatusEnum.SUCCESS);//退款已完成
            } else {
                refundRecord.setRefundStatus(RefundStatusEnum.FAIL);//退款失败
            }
            return true;
        }
        // 如果响应不成功，抛出公共异常
        throw new LogicException( NATIVE_REFUND_FAIL.getValue());
    }

    /**
     * 查询退款交易状态
     * 通过调用微信支付接口查询退款交易的状态，并更新退款记录的相关信息
     *
     * @param refundRecord 退款记录对象，包含退款所需的信息
     * @return 返回一个布尔值，表示退款查询是否成功
     * @throws LogicException 如果调用微信接口出错或退款查询失败，抛出此异常
     */
    @Override
    public Boolean queryRefundTrading(RefundRecord refundRecord) throws LogicException {
        // 获取微信支付的client对象
        WechatPayHttpClient client = WechatPayHttpClient.get(refundRecord.getEnterpriseId());

        // 请求地址：交易号
        String apiPath = StrUtil.format("/v3/refund/domestic/refunds/{}", refundRecord.getRefundNo());

        WeChatResponse response;
        try {
            response = client.doGet(apiPath);
        } catch (Exception e) {
            // 记录错误日志，并抛出自定义异常
            log.error("调用微信接口出错！apiPath = {}", apiPath, e);
            throw new LogicException( TradingEnum.NATIVE_QUERY_REFUND_FAIL.getValue());
        }

        // 更新退款记录的状态码和消息
        refundRecord.setRefundCode(Convert.toStr(response.getStatus()));
        refundRecord.setRefundMsg(response.getBody());
        if (response.isOk()) {
            // 解析响应的JSON对象
            JSONObject jsonObject = JSONUtil.parseObj(response.getBody());
            refundRecord.setRefundId(jsonObject.getStr("refund_id"));//第三方支付的退款单号
            // 根据退款状态更新退款记录的状态
            String status = jsonObject.getStr("status");
            if (StrUtil.equals(status, TradingConstant.WECHAT_REFUND_PROCESSING)) {
                refundRecord.setRefundStatus(RefundStatusEnum.SENDING);//退款中状态
            } else if (StrUtil.equals(status, TradingConstant.WECHAT_REFUND_SUCCESS)) {
                refundRecord.setRefundStatus(RefundStatusEnum.SUCCESS);//成功状态
            } else {
                refundRecord.setRefundStatus(RefundStatusEnum.FAIL);//失败状态
            }
            return true;
        }
        // 如果响应不为OK，抛出自定义异常
        throw new LogicException( TradingEnum.NATIVE_QUERY_REFUND_FAIL.getValue());
    }
}
